package org.jeecg.common.util;

import lombok.experimental.UtilityClass;
import org.apache.commons.collections4.map.MultiKeyMap;
import org.springframework.cglib.beans.BeanCopier;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * The utility for javabean properties copying.
 *
 * <p>Base on {@link BeanCopier} from cglib
 * <p>实现简单对象属性复制，复杂场景推荐Mapstruct。
 * <p>注意：属性名称相同而类型不同的属性不会被拷贝。
 *
 * @author Kai
 */
@UtilityClass
public class BeanUtils {
    private static final MultiKeyMap<Class<?>, BeanCopier> BEAN_TYPE_CACHE = new MultiKeyMap<>();

    /**
     * 对象属性复制
     *
     * @param <T>  javabean类型
     * @param from 源对象
     * @param to   目标类型
     * @return 目标类型对象
     */
    public static <T> T copy(Object from, Class<T> to) {
        if (from == null) {
            return null;
        }

        T toObj = ReflectUtils.newInstance(to);
        
        // 特殊处理Map类型的源对象
        if (from instanceof Map) {
            copyFromMap((Map<?, ?>) from, toObj);
        } else {
            getCopier(from.getClass(), to).copy(from, toObj, null);
        }
        
        return toObj;
    }

    /**
     * 批量对象属性复制
     *
     * @param <T>  javabean类型
     * @param from 源对象集合
     * @param to   目标类型
     * @return 目标类型对象列表
     */
    public static <T> List<T> copyMany(Collection<?> from, Class<T> to) {
        if (from == null || from.isEmpty()) {
            return null;
        }

        List<T> results = new ArrayList<>();
        BeanCopier copier = getCopier(from.stream().findFirst().get().getClass(), to);
        for (Object obj : from) {
            T toObj = ReflectUtils.newInstance(to);
            copier.copy(obj, toObj, null);
            results.add(toObj);
        }

        return results;
    }

    private static BeanCopier getCopier(Class<?> from, Class<?> to) {
        BeanCopier copier = BEAN_TYPE_CACHE.get(from, to);
        if (copier == null) {
            copier = BeanCopier.create(from, to, false);
            BEAN_TYPE_CACHE.put(from, to, copier);
        }

        return copier;
    }

    /**
     * 从Map复制属性到JavaBean对象
     *
     * @param map    源Map对象
     * @param target 目标JavaBean对象
     * @param <T>    目标对象类型
     */
    private static <T> void copyFromMap(Map<?, ?> map, T target) {
        if (map == null || target == null) {
            return;
        }

        for (Map.Entry<?, ?> entry : map.entrySet()) {
            Object key = entry.getKey();
            Object value = entry.getValue();
            
            if (key != null && value != null) {
                String fieldName = key.toString();
                try {
                    ReflectUtils.setFieldValue(target, fieldName, value);
                } catch (Exception e) {
                    // 忽略无法设置的字段
                }
            }
        }
    }

    /**
     * 根据返回字段数组对象和数据数组对象转换到对应得字段上
     * @param fieldArray 字段数组对象
     * @param dataArray 数据数组对象
     * @return 实体对象数据
     * @param <T> 实体对象
     * @throws IllegalAccessException 对象反射异常
     */
    public static <T> T mapArrayToObject(Object[] fieldArray, Object[] dataArray, T object) throws IllegalAccessException {
        for (int i = 0; i < fieldArray.length; i++) {
            String fieldName = (String)fieldArray[i];
            Field field = ReflectUtils.getField(object.getClass(),fieldName);
            Object obj = dataArray[i];
            if (field != null && obj != null) {
                field.setAccessible(true);
                try {
                    // 如果字段类型和数据类型不匹配，尝试进行类型转换
                    if (field.getType().isAssignableFrom(obj.getClass())) {
                        field.set(object, obj);
                    } else if (obj instanceof java.util.Map && !field.getType().isPrimitive() && !field.getType().equals(String.class)) {
                        // 如果数据是Map类型而字段是复杂对象，使用BeanUtils.copy进行转换
                        Object convertedObj = copy(obj, field.getType());
                        field.set(object, convertedObj);
                    } else {
                        // 对于其他情况，直接设置值
                        field.set(object, obj);
                    }
                } catch (Exception e) {
                    // 如果转换失败，跳过该字段
                    System.err.println("Failed to set field " + fieldName + ": " + e.getMessage());
                }
            }
        }
        return object;
    }

}
